package com.hitqz.robot.biz.config;

/**
 * @author xupkun
 * @date 2024/12/1
 */

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.FileAppender;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.hitqz.robot.api.common.enums.SubscribeTopicEnum;
import com.hitqz.robot.biz.websocket.util.BroadcastTopicUtil;
import com.hitqz.robot.netty.service.Send2ServerService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 读取日志文件最后的数据
 */
@Slf4j
public class LogTailer {


    public static boolean hasPush = false;
    private static final ThreadPoolExecutor networkExecutor = new ThreadPoolExecutor(
            10,               // 核心线程数
            20,              // 最大线程数
            60L,             // 非核心线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new LinkedBlockingQueue<>(50),  // 阻塞队列
            Executors.defaultThreadFactory(), // 默认的线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略：提交任务的线程执行任务
    );

    public static void readLogTailer(String clientId) {
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        FileAppender fileAppender = (FileAppender) loggerContext.getLogger("ROOT").getAppender("info");
        if (fileAppender != null) {
            Path logPath = Paths.get(System.getProperty("user.dir"), fileAppender.getFile());
            log.info("读取日志文件的地址是：" + logPath);
            Send2ServerService send2ServerService = SpringUtil.getBean(Send2ServerService.class);
            networkExecutor.execute(new LogWatcher(logPath, clientId, send2ServerService));
        }
    }

    private static class LogWatcher implements Runnable {
        private final Path logPath;
        private final String clientId;
        private final Send2ServerService send2ServerService;
        private long lastModified = 0;

        public LogWatcher(Path logPath, String clientId, Send2ServerService send2ServerService) {
            this.logPath = logPath;
            this.clientId = clientId;
            this.send2ServerService = send2ServerService;
        }

        @Override
        public void run() {
            try {
                long lastModified = Files.getLastModifiedTime(logPath).toMillis();
                WatchService watcher = FileSystems.getDefault().newWatchService();
                WatchKey key =  logPath.getParent().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
                while (true) {
                    WatchKey wk = watcher.poll(1, TimeUnit.MINUTES);
                    if (wk != null) {
                        for (WatchEvent<?> event : wk.pollEvents()) {
                            WatchEvent.Kind<?> kind = event.kind();
                            if (kind == StandardWatchEventKinds.OVERFLOW) {
                                continue;
                            }
                            if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                                long newModified = Files.readAttributes(logPath, BasicFileAttributes.class).lastModifiedTime().toMillis();
                                if (newModified > lastModified) {
                                    lastModified = newModified;
                                    try (RandomAccessFile file = new RandomAccessFile(logPath.toFile(), "r")) {
                                        String lastLine = readLastLine(file);
                                        BroadcastTopicUtil.broadcastMessageByRandomId(
                                                null,
                                                SubscribeTopicEnum.ROBOT_PUSH_INFO_LOG,
                                                removeAnsiEscapes(lastLine));
                                        if (StrUtil.isNotBlank(clientId)) {
                                            send2ServerService.sendLog(clientId, null, removeAnsiEscapes(lastLine));
                                        }
                                    } catch (IOException e) {
                                        log.error("读取日志文件失败", e);
                                    }
                                }
                            }
                        }
                        wk.reset();
                    }
                }
            } catch (IOException | InterruptedException e) {
                log.error("文件监听失败", e);
            }
        }

        private String readLastLine(RandomAccessFile file) throws IOException {
            StringBuilder sb = new StringBuilder();
            long fileLength = file.length() - 1;
            byte[] buffer = new byte[4096];  // 定义一个合适的缓冲区大小，可根据实际调整
            int bufferIndex = 0;
            for (long filePointer = fileLength; filePointer >= 0; filePointer--) {
                file.seek(filePointer);
                byte readByte = file.readByte();
                if (readByte == 0xA || readByte == 0xD) {
                    if (filePointer == fileLength || (readByte == 0xD && filePointer == fileLength - 1)) {
                        continue;
                    }
                    break;
                }
                buffer[bufferIndex++] = readByte;
                if (bufferIndex == buffer.length) {  // 缓冲区满了则进行转换和添加操作
                    sb.append(new String(buffer, 0, bufferIndex, StandardCharsets.UTF_8));
                    bufferIndex = 0;
                }
            }
            if (bufferIndex > 0) {  // 处理剩余未处理的字节
                sb.append(new String(buffer, 0, bufferIndex, "UTF-8"));
            }
            return sb.reverse().toString();
        }
    }


    public static void pushNow(String clientId, String randomId) {
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        // 获取名为"FILE"的Appender（假设你的日志输出到名为FILE的Appender）
        FileAppender fileAppender = getLoggerFileAppender(loggerContext, "info");
        Send2ServerService send2ServerService = SpringUtil.getBean(Send2ServerService.class);

        if (fileAppender != null) {
            networkExecutor.execute(() -> {
                try {
                    Path logPath = Paths.get(fileAppender.getFile());
                    try (BufferedReader reader = Files.newBufferedReader(logPath)) {
                        String line;
                        int i = 0;
                        while ((line = reader.readLine()) != null) {
                            try {
                                line = removeAnsiEscapes(line);
                                BroadcastTopicUtil.broadcastMessageByRandomId(
                                        Integer.valueOf(randomId),
                                        SubscribeTopicEnum.ROBOT_PUSH_INFO_LOG,
                                        line);
                                if (StrUtil.isNotBlank(clientId)) {
                                    send2ServerService.sendLog(clientId, randomId, line);
                                }
                            } catch (Exception e) {
                                log.error("Error processing log line: " + e.getMessage(), e);
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("Exception occurred while reading log file: " + e.getMessage(), e);
                }
            });

            LogTailer.readLogTailer(clientId);
            //synchronized (LogTailer.class) {
            //    if (!hasPush) {
            //
            //        hasPush = false;
            //    }
            //}
        }
    }

    private static FileAppender getLoggerFileAppender(LoggerContext context, String appenderName) {
        return (FileAppender) context.getLogger("ROOT").getAppender(appenderName);
    }
    private static String removeAnsiEscapes(String line) {
        // 实现去除ANSI转义字符的逻辑
        return line.replaceAll("\u001B\\[[;\\d]*m", "");
    }




}
